feat(copilot): add a docs-backed platform agent - #5948
Conversation
…ocs; serve openapi.json publicly - search_docs server tool: same vector search over docs_embeddings plus an optional docs/documentation/... VFS path prefix mapped onto a source_document scope (covers both <tail>.mdx and <tail>/... layouts); unscoped searches exclude academy/ and api-reference/ rows so the scope is exactly the Documentation tab - @docs chat context repointed to the new tool; display label updated - apps/docs now serves /openapi.json so the mothership can build its docs/api-reference/<tag>.json VFS views from the deployed spec - generated tool catalog/schemas regenerated from the mothership contract Companion: simstudioai/mothership feat/enhance-search-agent Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tool chips
read("docs/documentation/workflows/index.mdx") now renders "Read
Workflows/index" instead of the leaf-only fallback ("Read Index").
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
"Searched docs" becomes 'Searched docs for "<query>"' (toolTitle/title preferred, query fallback, truncated at 60 chars). Also adds the missing browser_list_sessions display title the catalog regen surfaced. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A 400 from the chat POST's ChatMessageSchema parse returned the zod issues to the client but logged nothing, making "message disappears on send" undiagnosable from server logs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A workspace_resource tag emitted with id:"" resolved through `??` chains (which keep empty strings), got persisted onto the chat's resources, and then every send attached it and failed the chat request's id min-length validation — the message vanished on send, forever. Resolve chip ids by first non-empty candidate and drop id-less resources when building request attachments. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…search_docs Replaces the mothership's runtime docs corpus (llms.txt + llms-full.txt + openapi.json behind a 15m TTL cache) with a static manifest generated from the docs source, plus live per-page fetches. ~1,000 fewer lines of hand- written code and one repo instead of two. - scripts/sync-docs-manifest.ts walks apps/docs/content/docs/en and emits lib/copilot/generated/docs-manifest.ts. Each entry is simultaneously the docs/ VFS path and the docs.sim.ai URL path, so a read is a plain fetch. Section index pages fold onto their parent (fumadocs serves /workflows, not /workflows/index); academy/ and api-reference/ are excluded — they stay unmounted and unsearchable, reachable only via scrape_page. - docs-manifest:generate / :check, with a CI step so a page added, renamed, or deleted without regenerating fails the build. Content edits don't. - lib/copilot/docs/docs-corpus.ts + tools/handlers/vfs.ts: glob matches the manifest with no network, read fetches the page live, grep takes exactly ONE page (each is a fetch, so there is no corpus-wide grep). Opt-in like uploads/ — only an explicit docs/ prefix ever matches. - search_docs now scopes to the docs/ tree instead of docs/documentation/, validates its path against the manifest (a bad path errors instead of silently returning nothing), and returns the docs/ path with every chunk so search chains into read. Unscoped searches drop rows the agent could not then read: unmounted sections, and pages gone since the last index rebuild. - @docs tagging disabled: its query was the raw user message, a poor embedding query, and the mention UI it fed was already dead code. - Reverts the apps/docs /openapi.json route, added only for the old api-reference VFS views. Companion: simstudioai/mothership feat/enhance-search-agent Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Multi-agent review of the docs/ VFS change. Applied the behavior-preserving fixes plus two agent-facing bugs that made real pages unreadable. - docs read no longer hard-fails on oversized pages. Six+ live integration references exceed the inline cap (github.mdx is 354KB, sportmonks 513KB), so a plain read of them ALWAYS failed and cost a second fetch to recover. Truncate to the largest whole-line prefix that fits, keep the true totalLines, and tell the model how to page. An explicit offset/limit that still overflows is still an error — that one is a caller mistake. - classify docs fetch failures. Everything collapsed to null, so a permanent 404 was reported to the agent as "temporarily unavailable, retry shortly", inviting a retry loop on a page that will never exist. 4xx (except 429) is now permanent and says so; 5xx/429/network/timeout keep the retry wording. - register search_documentation as a transitional alias for search_docs. sim and mothership deploy independently and the rename deleted the old id on both sides, so BOTH deploy orders broke docs lookup for the window between them. Old params are a subset of the new. Remove once both ship. - extract the index-page fold (X/index.mdx <-> X.mdx) into docs-path.ts. It was re-derived in three places — the manifest generator, the source_document reverse mapping, and the search scope filter — which is the hand-synced-duplicate shape that has drifted in this repo before. - grepDocsPage now goes through grepReadResult, the primitive files/ and uploads/ grep already use, instead of calling grep directly. - couldMatchDocsScope delegates to isDocsPath; the bodies were identical. - drop the dead 'docs' member from AgentContextType. Tests: 404-vs-5xx-vs-429 classification, network failure, and a docs-path round-trip asserting one source candidate reproduces every manifest entry. Verified: tsc clean, 932 copilot tests, biome clean, docs-manifest:check, check:utils and check:api-validation:strict both pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The SQL LIMIT is applied before the similarity-threshold and liveness filters, so search_docs can return fewer hits than topK — or none, when every candidate was filtered. An empty array is indistinguishable from "the documentation does not cover this", which sends the agent off to guess instead of rephrasing or falling back to glob. searchDocs now returns the drop counts alongside the results, and the tool attaches a note when anything was dropped: how many candidates the index returned, why they went, and what to try next. Silent on the common path. This does not change which rows are returned or how many — the ordering issue behind the shortfall is a pre-existing bug the deleted search-documentation.ts had too, and pushing the threshold into SQL is its own change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview @docs chat contexts stay on the wire but are inert on the server until tagging is revived via Reviewed by Cursor Bugbot for commit 16e2080. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
Greptile SummaryThe PR completes the documentation-tool migration and repairs the previously reported mixed-version dispatch path.
Confidence Score: 5/5The PR appears safe to merge. The previously reported transitional-alias dispatch failure is fully addressed, and no blocking failure remains.
|
| Filename | Overview |
|---|---|
| apps/sim/lib/copilot/tools/server/router.ts | Registers both tool identifiers against the new documentation-search handler, completing the previously missing dispatch path. |
| apps/sim/lib/copilot/tools/server/docs/search-docs-alias.test.ts | Verifies that the legacy identifier remains known, Sim-routed, registered, hidden, and compatible with legacy parameters. |
| apps/sim/lib/copilot/generated/tool-catalog-v1.ts | Adds the new tool and preserves the old identifier as a hidden transitional catalog entry. |
| apps/sim/lib/copilot/docs/docs-corpus.ts | Implements the manifest-backed documentation VFS namespace, live reads, globbing, and single-page grep. |
| apps/sim/lib/copilot/docs/docs-search.ts | Implements manifest-aware semantic documentation search with path scoping and stale-result filtering. |
Reviews (4): Last reviewed commit: "fix(copilot): make the search_docs topK ..." | Re-trigger Greptile
The registry alias added for the rename never fired: executeTool gates on isKnownTool(toolId) — membership in the generated TOOL_CATALOG — before it consults baseServerToolRegistry, so an id absent from the catalog is rejected as unknown and routed to the app-tool path instead. The alias was dead code, and worse, it advertised a mixed-version safety net that did not exist. The companion PR restores search_documentation to the contract as a hidden, deprecated entry, so the id now passes the catalog gate and reaches this alias. Marks it hidden in the UI set too — a call only ever arrives from an older Mothership build and renders as the search_docs it maps onto. Adds a test pinning every link in the dispatch chain (catalog membership, sim route, registered handler, hidden, param shape) — the assertion that would have caught this. Remove all of it once search_docs is live in prod on both sides.
|
Confirmed — you're right, and this was a real hole rather than a nit.
const canUseRegisteredHandler =
isKnownTool(toolId) && (isSimExecuted(toolId) || ...)
if (!canUseRegisteredHandler) return executeAppTool(toolId, appParams) // → tool not foundSo dropping Fixed across both repos:
One thing your flowchart implies that I want to state explicitly: this only fixes the sim-first direction. New Mothership emitting 940 copilot tests, tsc, and all 11 CI audits pass. |
|
@cursor review |
…g search A directory scope matched only `<section>/%`, which covers an overview stored as `<section>/index.mdx` but not one stored as a sibling `<section>.mdx`. Fumadocs accepts both layouts and page scope already handles both via docsSourceCandidates, so a scoped section search could silently omit the overview chunks — and the doc comment claimed it did not. Every section in the tree currently uses the index.mdx layout, so nothing is broken today; this closes the gap before someone adds a sibling overview and gets quietly incomplete results.
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit e52205d. Configure here.
The clamp guarded magnitude but not type: Math.min/Math.max propagate NaN, so a non-numeric topK reached the query as `.limit(NaN)`. The `?? DEFAULT` only caught undefined. Nothing enforced this but the generated Ajv schema, and searchDocs is also called directly, so it should not depend on that. Extract clampTopK, which falls back to the default for anything non-finite (NaN, Infinity, a string that slipped through) and clamps the rest to [1, 25]. The clamp was completely untested because the db mock's .limit() stub discarded its argument — the mock now records it. Covers default, cap, floor, truncation, and the non-finite fallback. Worth pinning: staging's search_documentation documented "max 10" and enforced nothing, so this bound is new behavior, not just a bigger number.
Resolution notes: - generated tool catalog/schemas regenerated from the merged mothership contract (staging's browser/terminal agents + this branch's search_docs and lean search task description) - hidden-tools comments merged (load_skill from staging + the two transitional shim notes from this branch) - docs manifest regenerated: staging added integrations/managed_agent, integrations/tiktok, and platform/enterprise/self-hosted pages Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| 35187658 | Triggered | Username Password | ac8eecc | apps/desktop/src/main/browser-credentials/vault.test.ts | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secret safely. Learn here the best practices.
- Revoke and rotate this secret.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
…tions outright, no shims The transitional apparatus is gone: no search_documentation registry alias, no get_platform_actions handler, and the ids are out of the regenerated catalog/schemas. During the deploy window an old Mothership build calling either id gets the recoverable tool-not-found result. The two ids stay in HIDDEN_TOOL_NAMES forever — like load_agent_skill, historical persisted chats contain their tool calls and must replay without rendering chips for retired tools. The alias test is replaced by a dispatch test pinning search_docs's own catalog -> route -> handler chain and the retired ids' gone-but-chip-hidden state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| return { output: { content, totalLines: page.totalLines }, returnedLines: kept } | ||
| } | ||
| kept = Math.floor(kept / 2) | ||
| } |
There was a problem hiding this comment.
Truncation skips larger fitting prefixes
Medium Severity
truncateDocsPageToInlineCap is documented as returning the largest whole-line prefix that fits the inline budget, but it only halves kept and returns on the first fit. Pages just over the cap can come back at roughly half size even when a much larger prefix would fit, forcing extra read/grep round trips for large docs pages.
Reviewed by Cursor Bugbot for commit a30ad25. Configure here.
…_docs revival The 'docs' ChatContext kind stays in the wire schema and the panel union but resolves to nothing server-side. Comments at all three layers now say why it is kept: a future @docs mention should be rebuilt on search_docs, not by reintroducing the kind or reviving the retired whole-message pre-search. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e case The static TOOL_TITLES entry is unreachable for search_docs — the dynamic switch case returns first so it can include the query — so the rename only takes effect there. Tests updated to the new wording. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5195f91 to
c7bfb61
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 1455ed4. Configure here.
| // read cost the same single fetch, so grep is an alternative to a read here, | ||
| // never a step before one. | ||
| const notice = (shown: number) => | ||
| `\n\n[Page truncated: returned lines 1-${shown} of ${page.totalLines}. To continue, read this path with offset: ${shown}. To jump straight to a section, grep this path INSTEAD of reading it — grep is the same single fetch and returns only matching lines with their numbers.]` |
There was a problem hiding this comment.
Docs paging guidance contradicts handler
Medium Severity
Truncation tells the model to continue a large docs read with only offset, but any defined offset/limit disables further truncation and fails when the remaining page still exceeds the inline cap. Following the notice on oversized integration pages therefore errors instead of returning the next chunk.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 1455ed4. Configure here.


Summary
Copilot can now delegate Sim product questions to a dedicated platform agent. Documented behavior is grounded in Sim's public docs, while workspace-access questions can use the live
list_user_workspacestool supplied by the focused dependency in #6227.docs/): a generated manifest synced fromapps/docs/content/docs/endefines a read-only VFS tree.globlists it,readfetches pages from docs.sim.ai, and single-pagegrepsearches within a page. Academy and API-reference remain deliberately unmounted.search_docsserver tool replacessearch_documentation: vector search supports optional page/sectionpathscoping and boundedtopK, and reports filtered-result shortfalls explicitly.search_documentationandget_platform_actionsids stay permanently chip-hidden for historical chat replay.@Docstagging remains inert: the wire context kind stays as a documented future seam, but the server continues to drop it; the platform agent uses the docs VFS andsearch_docsinstead.Depends on #6227 for the updated
list_user_workspacesresult. The companion Mothership agent consumes that result in simstudioai/mothership#371, which in turn depends on simstudioai/mothership#393.Type of Change
Testing
bun --cwd apps/sim test lib/copilot/tools/handlers/workflow/queries.test.ts lib/copilot/tools/tool-display.test.ts— 74 tests passed across this branch and its focused dependency.bun run check:api-validationpassed.bunx biome checkpassed for the changed Sim files.bun run scripts/sync-tool-catalog.ts --check --input=../mothership/copilot/contracts/tool-catalog-v1.jsonpassed.docs-manifest:checkprevents manifest drift in CI.Checklist
Screenshots/Videos
UI impact is chip text only: docs reads render as
Section/page(for example, "Read Workflows/agent") and docs searches as "Searching Sim docs for "..."".Related: simstudioai/mothership#371
Post-Deploy Monitoring & Validation
Merge and deploy #6227 and simstudioai/mothership#393 before the platform-agent PRs. For the first 24 hours after both platform PRs deploy, the Copilot/platform owner should watch request logs for
platform,list_user_workspaces, tool-not-found or denied failures, and repeated delegation. Healthy behavior is a direct workspace-list call for access and role questions, exactly one current workspace in scoped chats, and docs-backed answers for product behavior. Removing the platform allowlist entry in the companion PR disables the live lookup independently.